{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Azure AI Search with LangChain and Ragas\n",
    "\n",
    "This code demonstrates how to use Azure AI Search with OpenAI, Ragas, and Langchain. We will use [Ragas](https://docs.ragas.io/en/latest/index.html) to evaluate a [Langchain AI Search vector store](https://python.langchain.com/v0.1/docs/integrations/vectorstores/azuresearch/) on the following metrics:\n",
    "\n",
    "1. [Context Precision](https://docs.ragas.io/en/latest/concepts/metrics/context_precision.html) - whether the chunks relevant to answer the question are present in the context and ranked highly.\n",
    "1. [Context Recall](https://docs.ragas.io/en/latest/concepts/metrics/context_recall.html) - whether the retrieved chunks align with the ground truth answer in our evaluation test set of questions.\n",
    "\n",
    "The notebook covers the following tasks:\n",
    "1. Setup a sample data source using [integrated vectorization](https://learn.microsoft.com/en-us/azure/search/vector-search-integrated-vectorization). The [skillset](https://learn.microsoft.com/en-us/azure/search/cognitive-search-working-with-skillsets) uses two embedding models for our evaluation: [text-embedding-ada-002 and text-embedding-3-large](https://learn.microsoft.com/azure/ai-services/openai/concepts/models#embeddings)\n",
    "1. Sets up multiple [basic LangChain RAG pipelines](https://python.langchain.com/v0.1/docs/modules/chains/) to answer questions. These pipelines are customized to compare the following factors during evaluation:\n",
    "  1. What kind of embedding model are we using? text-embedding-ada-002 or text-embedding-3-large?\n",
    "  1. What search method are we using? [Vector search](https://learn.microsoft.com/azure/search/vector-search-overview), [hybrid search](https://learn.microsoft.com/azure/search/hybrid-search-overview), or [hybrid search with semantic reranking](https://learn.microsoft.com/azure/search/semantic-search-overview)?\n",
    "1. Graphs the comparison using the Context Precision and Context Recall metrics to compare the various embedding models and search methods."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Set up a Python virtual environment in Visual Studio Code\n",
    "\n",
    "1. Open the Command Palette (Ctrl+Shift+P).\n",
    "1. Search for **Python: Create Environment**.\n",
    "1. Select **Venv**.\n",
    "1. Select a Python interpreter. Choose 3.10 or later.\n",
    "\n",
    "It can take a minute to set up. If you run into problems, see [Python environments in VS Code](https://code.visualstudio.com/docs/python/environments)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Install packages\n",
    "\n",
    "If you get an OS permission error, add `--user` to the command line."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "! pip install -r ragas-requirements.txt --quiet"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Load .env file\n",
    "\n",
    "Copy `/code/.env-sample` to an `.env` file in the sample folder, and update accordingly. The search service and Azure OpenAI resource and model must exist, but the search index is created and loaded during code execution. Provide a unique name for the index. Endpoints and API keys can be found in the Azure portal."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from azure.identity import DefaultAzureCredential\n",
    "import os\n",
    "from azure.search.documents.indexes import SearchIndexClient, SearchIndexerClient\n",
    "\n",
    "load_dotenv(override=True) # take environment variables from .env.\n",
    "\n",
    "# Variables not used here do not need to be updated in your .env file\n",
    "endpoint = os.environ[\"AZURE_SEARCH_SERVICE_ENDPOINT\"]\n",
    "key_credential = os.environ[\"AZURE_SEARCH_ADMIN_KEY\"] if len(os.environ[\"AZURE_SEARCH_ADMIN_KEY\"]) > 0 else None\n",
    "index_name = os.getenv(\"AZURE_SEARCH_INDEX\", \"ragas-eval\")\n",
    "azure_openai_endpoint = os.environ[\"AZURE_OPENAI_ENDPOINT\"]\n",
    "azure_openai_key = os.environ[\"AZURE_OPENAI_KEY\"] if len(os.environ[\"AZURE_OPENAI_KEY\"]) > 0 else None\n",
    "azure_openai_ada002_embedding_deployment = os.environ[\"AZURE_OPENAI_ADA002_EMBEDDING_DEPLOYMENT\"]\n",
    "azure_openai_3_large_embedding_deployment = os.environ[\"AZURE_OPENAI_3_LARGE_EMBEDDING_DEPLOYMENT\"]\n",
    "azure_openai_api_version = os.environ[\"AZURE_OPENAI_API_VERSION\"]\n",
    "azure_openai_critic_deployment = os.environ[\"AZURE_OPENAI_CRITIC_DEPLOYMENT\"]\n",
    "azure_openai_generator_deployment = os.environ[\"AZURE_OPENAI_CRITIC_DEPLOYMENT\"]\n",
    "blob_connection_string = os.environ[\"BLOB_CONNECTION_STRING\"]\n",
    "# search blob datasource connection string is optional - defaults to blob connection string\n",
    "# This field is only necessary if you are using MI to connect to the data source\n",
    "# https://learn.microsoft.com/azure/search/search-howto-indexing-azure-blob-storage#supported-credentials-and-connection-strings\n",
    "search_blob_connection_string = os.getenv(\"SEARCH_BLOB_DATASOURCE_CONNECTION_STRING\", blob_connection_string)\n",
    "blob_container_name = os.getenv(\"BLOB_CONTAINER_NAME\", \"ragas-eval\")\n",
    "credential = key_credential or DefaultAzureCredential()\n",
    "\n",
    "indexer_client = SearchIndexerClient(endpoint, credential)\n",
    "index_client = SearchIndexClient(endpoint, credential)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup sample data\n",
    "\n",
    "For integrated vectorization to work, make sure the sample documents have been uploaded to your blob storage account"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Setup sample data in ragas-eval\n"
     ]
    }
   ],
   "source": [
    "from lib.utils import upload_sample_documents\n",
    "\n",
    "upload_sample_documents(\n",
    "    blob_connection_string=blob_connection_string,\n",
    "    blob_container_name=blob_container_name,\n",
    "    # Set to false if you want to use credentials included in the blob connection string\n",
    "    # Otherwise your identity will be used as credentials\n",
    "    use_user_identity=True\n",
    ")\n",
    "print(f\"Setup sample data in {blob_container_name}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup integrated vectorization\n",
    "\n",
    "Create a data source, skillset, index, and indexer to start the integrated vectorization process.\n",
    "The skillset vectorizes the data twice, once using text-embedding-ada-002 and again using text-embedding-3-large. The vectors are sent to different fields to enable comparison later on in the notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Data source 'ragas-eval-blob' created\n",
      "Index ragas-eval created\n",
      "Skillset ragas-eval-skillset created\n",
      "ragas-eval-indexer is created and running. If queries return no results, please wait a bit and try again.\n"
     ]
    }
   ],
   "source": [
    "from lib.utils import create_sample_index, create_sample_datasource, create_sample_skillset, create_sample_indexer\n",
    "\n",
    "# Create a data source \n",
    "datasource_result = create_sample_datasource(\n",
    "    indexer_client,\n",
    "    blob_container_name,\n",
    "    index_name,\n",
    "    search_blob_connection_string\n",
    ")\n",
    "print(f\"Data source '{datasource_result.name}' created\")\n",
    "\n",
    "# Create a index\n",
    "index = create_sample_index(\n",
    "    index_client,\n",
    "    index_name,\n",
    "    azure_openai_endpoint,\n",
    "    azure_openai_ada002_embedding_deployment,\n",
    "    azure_openai_3_large_embedding_deployment,\n",
    "    azure_openai_key)\n",
    "print(f\"Index {index.name} created\")\n",
    "\n",
    "# Create a skillset\n",
    "skillset = create_sample_skillset(\n",
    "    indexer_client,\n",
    "    index_name,\n",
    "    azure_openai_endpoint,\n",
    "    azure_openai_ada002_embedding_deployment,\n",
    "    azure_openai_3_large_embedding_deployment,\n",
    "    azure_openai_key\n",
    ")\n",
    "print(f\"Skillset {skillset.name} created\")\n",
    "\n",
    "# Create an run an indexer\n",
    "indexer = create_sample_indexer(\n",
    "    indexer_client,\n",
    "    index.name,\n",
    "    skillset.name,\n",
    "    datasource_result.name\n",
    ")\n",
    "print(f'{indexer.name} is created and running. If queries return no results, please wait a bit and try again.')  \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Initialize LangChain LLMs and embeddings\n",
    "\n",
    "In order for evaluation to work, we need the following:\n",
    "\n",
    "1. A LangChain embedding model for text-embedding-ada-002\n",
    "1. A LangChain embedding model for text-embedding-3-large\n",
    "1. A \"critic\" LLM used for judging our metrics. [gpt-4o](https://learn.microsoft.com/azure/ai-services/openai/concepts/models#gpt-4o-and-gpt-4-turbo) can be used as the critic.\n",
    "1. A \"generator\" LLM used for generating text. [gpt-35-turbo](https://learn.microsoft.com/azure/ai-services/openai/concepts/models#gpt-35) can be used as the generator."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from lib.utils import create_langchain_azure_openai_wrappers\n",
    "\n",
    "text_3_large_embeddings, ada_002_embeddings, generator_llm, critic_llm = create_langchain_azure_openai_wrappers(\n",
    "    azure_openai_api_version,\n",
    "    azure_openai_endpoint,\n",
    "    azure_openai_3_large_embedding_deployment,\n",
    "    azure_openai_ada002_embedding_deployment,\n",
    "    azure_openai_generator_deployment,\n",
    "    azure_openai_critic_deployment,\n",
    "    azure_openai_key\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Generate the test data set\n",
    "\n",
    "We can use [Ragas](https://docs.ragas.io/en/latest/getstarted/testset_generation.html) to generate a synthetic data set for evaluating our RAG pipeline from a sample of documents in your normal data set. The synthetic data set includes questions and answers generated from those documents.\n",
    "\n",
    "We have provided a [pre-generated test set](../../../data/evalquestions/ragas/testset.json) because generating test data may take a long time. You can use the provided code to generate a new test set based on your own data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "from datasets import Dataset\n",
    "from ragas.testset.generator import TestsetGenerator\n",
    "from ragas.testset.evolutions import simple, reasoning, multi_context\n",
    "from langchain_community.document_loaders import PyPDFDirectoryLoader\n",
    "\n",
    "documents_directory = os.path.join(\"..\", \"..\", \"..\", \"..\", \"data\", \"benefitdocs\")\n",
    "test_dataset_path = os.path.join(\"..\", \"..\", \"..\", \"..\", \"data\", \"evalquestions\", \"ragas\", \"testset.json\")\n",
    "if os.path.exists(test_dataset_path):\n",
    "    # Use a pre-generated test dataset by default\n",
    "    test_dataset = pd.read_json(test_dataset_path)\n",
    "    test_dataset = Dataset.from_pandas(test_dataset)\n",
    "else:\n",
    "    # The following code generates a synthetic data set based on a set of PDF documents in the sample directory\n",
    "    loader = PyPDFDirectoryLoader(path=documents_directory, glob=\"*.pdf\")\n",
    "    documents = loader.load()\n",
    "\n",
    "    generator = TestsetGenerator.from_langchain(\n",
    "        generator_llm,\n",
    "        critic_llm,\n",
    "        text_3_large_embeddings\n",
    "    )\n",
    "\n",
    "    # Change resulting question type distribution\n",
    "    # https://docs.ragas.io/en/latest/concepts/testset_generation.html\n",
    "    distributions = {\n",
    "        simple: 0.5,\n",
    "        multi_context: 0.4,\n",
    "        reasoning: 0.1\n",
    "    }\n",
    "\n",
    "    test_dataset = generator.generate_with_langchain_docs(documents, test_size=10, distributions=distributions) \n",
    "    test_dataset = test_dataset.to_dataset()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create RAG chains and evaluate them\n",
    "\n",
    "The following code demonstrates how to create a simple RAG chain in Langchain using the AI Search vector retriever, and then evaluate them using various RAGAS metrics. By default, Context Recall and Context Precision are used."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.llms.base import BaseLLM\n",
    "from langchain.chains import create_retrieval_chain\n",
    "from langchain.chains.combine_documents import create_stuff_documents_chain\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_core.vectorstores import VectorStore\n",
    "from datasets import Dataset\n",
    "from langchain.embeddings.base import Embeddings\n",
    "from ragas.metrics import (\n",
    "    ContextPrecision,\n",
    "    ContextRecall\n",
    ")\n",
    "from ragas.metrics.base import Metric\n",
    "from ragas.llms import LangchainLLMWrapper\n",
    "from ragas import evaluate\n",
    "from langchain_core.runnables import Runnable\n",
    "from typing import Optional, List\n",
    "\n",
    "def create_rag_chain(generator_llm: BaseLLM, vectorstore: VectorStore):\n",
    "    system_prompt = (\n",
    "        \"Use the given context to answer the question. \"\n",
    "        \"If you don't know the answer, say you don't know. \"\n",
    "        \"Use three sentence maximum and keep the answer concise. \"\n",
    "        \"Context: {context}\"\n",
    "    )\n",
    "    prompt = ChatPromptTemplate.from_messages(\n",
    "        [\n",
    "            (\"system\", system_prompt),\n",
    "            (\"human\", \"{input}\"),\n",
    "        ]\n",
    "    )\n",
    "    question_answer_chain = create_stuff_documents_chain(generator_llm, prompt)\n",
    "    return create_retrieval_chain(vectorstore.as_retriever(), question_answer_chain)\n",
    "\n",
    "def evaluate_rag_chain(test_dataset: Dataset, critic_llm: BaseLLM, embeddings: Embeddings, rag_chain: Runnable, metrics: Optional[List[Metric]] = None):\n",
    "    predictions = rag_chain.batch([{\"input\": question} for question in test_dataset[\"question\"]])\n",
    "    eval_dataset = Dataset.from_list(\n",
    "    [\n",
    "        {\n",
    "            \"question\": test_dataset[i][\"question\"],\n",
    "            \"contexts\": [context.page_content for context in prediction[\"context\"]],\n",
    "            \"answer\": prediction[\"answer\"],\n",
    "            \"ground_truth\": test_dataset[i][\"ground_truth\"]\n",
    "        } for i, prediction in enumerate(predictions)\n",
    "    ])\n",
    "    metrics = metrics or [\n",
    "        ContextPrecision(LangchainLLMWrapper(critic_llm)),\n",
    "        ContextRecall(LangchainLLMWrapper(critic_llm)),\n",
    "    ]\n",
    "    return evaluate(\n",
    "        dataset=eval_dataset,\n",
    "        metrics=metrics,\n",
    "        llm=critic_llm,\n",
    "        embeddings=embeddings)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Evaluate text-embedding-ada-002 context recall and precision\n",
    "\n",
    "Use the evaluation and chain creation methods we created in the previous cell to run the evaluation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "4c9a2fb3fb424f0aa56100ef853c727e",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Evaluating:   0%|          | 0/20 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "{'context_precision': 0.7333, 'context_recall': 0.7488}"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from lib.utils import create_langchain_vectorstore\n",
    "\n",
    "ada_002_qa = create_rag_chain(\n",
    "    generator_llm=generator_llm,\n",
    "    vectorstore=create_langchain_vectorstore(\n",
    "        azure_search_endpoint=endpoint,\n",
    "        azure_search_key=key_credential,\n",
    "        index_name=index_name,\n",
    "        embedding_function=ada_002_embeddings.embed_query,\n",
    "        vector_field_name=\"vector_ada002\"\n",
    "    )\n",
    ")\n",
    "\n",
    "ada_002_eval = evaluate_rag_chain(\n",
    "    test_dataset,\n",
    "    critic_llm,\n",
    "    ada_002_embeddings,\n",
    "    ada_002_qa)\n",
    "\n",
    "ada_002_eval"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Evaluate text-embedding-3-large context recall and precision\n",
    "\n",
    "Use the evaluation and chain creation methods we created in the previous cell to run the evaluation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "a726c27acd494b67b8a5abe638cce003",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Evaluating:   0%|          | 0/20 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "{'context_precision': 0.7389, 'context_recall': 0.8714}"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from lib.utils import create_langchain_vectorstore\n",
    "\n",
    "text_3_large_qa = create_rag_chain(\n",
    "    generator_llm=generator_llm,\n",
    "    vectorstore=create_langchain_vectorstore(\n",
    "        azure_search_endpoint=endpoint,\n",
    "        azure_search_key=key_credential,\n",
    "        index_name=index_name,\n",
    "        embedding_function=text_3_large_embeddings.embed_query,\n",
    "        vector_field_name=\"vector_3_large\"\n",
    "    )\n",
    ")\n",
    "\n",
    "text_3_large_eval = evaluate_rag_chain(\n",
    "    test_dataset,\n",
    "    critic_llm,\n",
    "    text_3_large_embeddings,\n",
    "    text_3_large_qa)\n",
    "\n",
    "text_3_large_eval"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Visualize the results\n",
    "\n",
    "Use matplotlib to graph the two metrics together for comparison. We can see that text-embedding-3-large has superior context recall on the test set compared to text-embedding-ada-002"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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BpoxY4bi4+Pt+txoFODMmTMZpqffMPnaW8+kc3FxUZMmTfR///d/dofBjh07pjlz5qhevXq5cjgpfVTj2vqufd9kR7NmzSQpQ1i/lWVK/56H9eKLL2rJkiW2XxRp166dYmJitGTJkgz9z549qytXrkiSnn76aRljNHz48Az90rc9s32RkJCgmTNn3lLdV+vVq5cKFy6sAQMGaPfu3RmmHz9+XO+9954kKSwsTG5ubpo0aZJdTZ999pkSEhJu+SrjzEyePNnu+Ycffijpf6/p7dpH195ax8fHR6VLl7Z9PgoXLqyqVatq9uzZdt8L27Zt09KlS23fd4DEiB3uIaVKldKcOXPUvn17lS9f3u6XJ9auXauvv/7adm+oKlWqqEuXLpo2bZrOnj2rhg0basOGDZo9e7Zat26txo0bZ3v9VatWlYuLi0aPHq2EhAS5u7vb7mc1ZcoUPf/886pevbqeffZZBQQEKD4+XosWLVLdunX10Ucf6dKlS+rSpYvKlCljuzXI8OHDtXDhQkVERGjr1q228BgaGqqff/5Z48aNU5EiRVSiRIkMt/fILb6+vlmqX/o3rNSrV0/Vq1dXz549VaJECR08eFCLFi267s+XzZ49Wx9//LHatGmjUqVK6dy5c5o+fbp8fX1v+A/Se++9p2XLlqlevXrq06eP8uTJo08++UTJycl6//33c23bGzRooPfff1+XL19W0aJFtXTpUtu9/3KiatWq6tChgz7++GMlJCSoTp06Wr58ufbu3XvL9b766quaMGGCRo0apblz5+q1117T999/rxYtWthuz5OUlKStW7fqm2++0cGDB1WwYEE1btxYzz//vCZNmqQ9e/bYDjOvXr1ajRs3Vt++fdWkSRO5ubmpZcuWevHFF3X+/HlNnz5dhQoV0pEjR265dunf87y+++47PfHEE6patardL0/Exsbqv//9r200PiAgQIMHD9bw4cPVtGlTtWrVSrt27dLHH3+shx9+2O7mzbnlwIEDatWqlZo2baqYmBjbLWuqVKkiSbdtH1WoUEGNGjVSaGio8ufPr02bNumbb75R3759bX0++OADNWvWTLVr11a3bt1stzvx8/OzxO8PIxc55Fpc4Bbs3r3b9OjRw4SEhBg3NzeTN29eU7duXfPhhx+aS5cu2fpdvnzZDB8+3JQoUcK4urqa4OBgM3jwYLs+xvx7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l1mVk9cAsLL77bvN09PT7ny88+fPy8XFRZs3b7YLp5Lk4+Njm+dGWrZsqeLFi2v69OkqUqSI0tLS9NBDDyklJeWWakXuY8QONmXKlJGnp6eWL1+eYVr58uW1ZcsW20m/0r8n2jo7O+vBBx/M8jrc3NyUmppq11a9enVt375dpUuXzvBIvwp17dq1Gj16tBYuXCgfHx/17dv3psu9mStXrmjTpk2257t27dLZs2dVvnz5685TvXp1HT16VHny5MlQa8GCBSVJlStXznQfXo+np6datmypSZMmacWKFYqJidHWrVsz9CtfvrwOHz5sd4HH9u3bdfbsWVWoUCHL6wPuN/fbd9u1qlWrptTUVB0/fjxDHUFBQZJu/L116tQp7dq1S0OGDNFjjz2m8uXL2y6qwN2HYAcbDw8PvfHGG3r99df1n//8R/v27dO6dev02WefqVOnTvLw8FCXLl20bds2/frrr3r55Zf1/PPPKzAwMMvrCAkJ0apVq/T333/r5MmTkqQ33nhDa9euVd++fRUXF6c9e/bo//7v/2xfcOfOndPzzz+vV155Rc2aNdOXX36pefPm6Ztvvrnhcm/G1dVVL7/8stavX6/Nmzera9eueuSRR1SzZs3rzhMWFqbatWurdevWWrp0qQ4ePKi1a9fqrbfesoXEqKgo/fe//1VUVJR27NihrVu3avTo0Zkub9asWfrss8+0bds27d+/X1988YU8PT1VvHjxTNddqVIlderUSbGxsdqwYYM6d+6shg0bqkaNGlnaZuB+dL99t12rbNmy6tSpkzp37qxvv/1WBw4c0IYNGxQdHa1FixZJkgYPHqyNGzeqT58++uOPP7Rz505NmTJFJ0+eVL58+VSgQAFNmzZNe/fu1S+//KLIyMgc1YI7wNEn+eHukpqaat577z1TvHhx4+rqaooVK2a7ovOPP/4wjRs3Nh4eHiZ//vymR48e5ty5c7Z5u3TpYp588km75b366qt2V23GxMSYypUrG3d3d3P122/Dhg3m8ccfNz4+Psbb29tUrlzZdhJxRESEqVSpkt0FC2PHjjX58+c3f/311w2Xez0zZ840fn5+Zv78+aZkyZLG3d3dhIWFmUOHDtn6REVFmSpVqmSYNzEx0bz88sumSJEixtXV1QQHB5tOnTrZXdQwf/58U7VqVePm5mYKFixonnrqKdu0q0+G/u6770ytWrWMr6+v8fb2No888oj5+eefM+1rzL8nQbdq1cp4e3ubvHnzmrZt29pO8L5ezePHjzfFixe/6T4BrOx++267VkpKihk6dKgJCQkxrq6upnDhwqZNmzbmjz/+sPVZsWKFqVOnjnF3dzf+/v4mPDzcdvHDsmXLTPny5Y27u7upXLmyWbFihZFkvvvuO2MMF0/cTZyMMcaBuRJwiFmzZqlfv3783A0AwFI4FAsAAGARBDtYUrNmzewu67/6MXLkSEeXBwA5wncbboZDsbCkv//+WxcvXsx0Wv78+ZU/f/47XBEA3Dq+23AzBDsAAACL4FAsAACARRDsAAAALIJgBwAAYBEEOwAAAIsg2AHAHeLk5KQFCxY4ugwAFkawA3Bf6dq1q5ycnNSrV68M01566SU5OTmpa9euWVrWihUr5OTklOVfMDly5IiaNWuWjWoBIHsIdgDuO8HBwZo7d67d/cAuXbqkOXPmqFixYrm+vpSUFElSUFCQ3N3dc335AJCOYAfgvlO9enUFBwfr22+/tbV9++23KlasmKpVq2ZrS0tLU3R0tEqUKCFPT09VqVJF33zzjSTp4MGDaty4sSQpX758diN9jRo1Ut++fdWvXz8VLFhQ4eHhkjIeiv3rr7/UoUMH5c+fX97e3qpRo4bWr18vSdqyZYsaN26svHnzytfXV6Ghodq0adPt3C0ALCCPowsAAEd44YUXNHPmTHXq1EmSNGPGDEVERGjFihW2PtHR0friiy80depUlSlTRqtWrdJzzz2ngIAA1atXT/Pnz9fTTz+tXbt2ydfXV56enrZ5Z8+erd69e2vNmjWZrv/8+fNq2LChihYtqu+//15BQUGKjY1VWlqaJKlTp06qVq2apkyZIhcXF8XFxcnV1fX27RAAlkCwA3Bfeu655zR48GAdOnRIkrRmzRrNnTvXFuySk5M1cuRI/fzzz6pdu7YkqWTJkvrtt9/0ySefqGHDhrafbypUqJD8/f3tll+mTBm9//77113/nDlzdOLECW3cuNG2nNKlS9umx8fH67XXXlO5cuVsywOAmyHYAbgvBQQEqHnz5po1a5aMMWrevLkKFixom753715duHBBjz/+uN18KSkpdodrryc0NPSG0+Pi4lStWrXr/rZnZGSkunfvrs8//1xhYWFq27atSpUqlYUtA3A/I9gBuG+98MIL6tu3ryRp8uTJdtPOnz8vSVq0aJGKFi1qNy0rF0B4e3vfcPrVh20zM2zYMHXs2FGLFi3STz/9pKioKM2dO1dt2rS56boB3L+4eALAfatp06ZKSUnR5cuXbRc4pKtQoYLc3d0VHx+v0qVL2z2Cg4MlSW5ubpKk1NTUbK+7cuXKiouL0+nTp6/bp2zZsurfv7+WLl2qp556SjNnzsz2egDcXwh2AO5bLi4u2rFjh7Zv3y4XFxe7aXnz5tXAgQPVv39/zZ49W/v27VNsbKw+/PBDzZ49W5JUvHhxOTk56YcfftCJEydso3xZ0aFDBwUFBal169Zas2aN9u/fr/nz5ysmJkYXL15U3759tWLFCh06dEhr1qzRxo0bVb58+VzdfgDWQ7ADcF/z9fWVr69vptPeffddvf3224qOjlb58uXVtGlTLVq0SCVKlJAkFS1aVMOHD9egQYMUGBhoO6ybFW5ublq6dKkKFSqkJ554QpUqVdKoUaPk4uIiFxcXnTp1Sp07d1bZsmXVrl07NWvWTMOHD8+VbQZgXU7GGOPoIgAAAHDrGLEDAACwCIIdAACARRDsAAAALIJgBwAAYBEEOwAAAIsg2AEAAFgEwQ4AAMAiCHYAAAAWQbADAACwCIIdAACARRDsAAAALIJgBwAAYBH/D7x0Hda84LXnAAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "labels = list(ada_002_eval.keys())\n",
    "ada_002_values = list(ada_002_eval.values())\n",
    "text_3_large_values = list(text_3_large_eval.values())\n",
    "\n",
    "x = np.arange(len(labels))  # the label locations\n",
    "width = 0.35  # the width of the bars\n",
    "\n",
    "# Plotting\n",
    "fig, ax = plt.subplots()\n",
    "rects1 = ax.bar(x - width/2, ada_002_values, width, label='ada-002')\n",
    "rects2 = ax.bar(x + width/2, text_3_large_values, width, label='text-3-large')\n",
    "\n",
    "# Adding labels, title, and custom x-axis tick labels, etc.\n",
    "ax.set_xlabel('Metrics')\n",
    "ax.set_ylabel('Scores')\n",
    "ax.set_title('Context Precision and Recall Comparison')\n",
    "ax.set_xticks(x)\n",
    "ax.set_xticklabels(labels)\n",
    "ax.set_ylim(0, 1)\n",
    "ax.legend()\n",
    "\n",
    "fig.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Evaluate search methods on text-embedding-ada-003\n",
    "\n",
    "Further evaluate text-embedding-ada-003 by comparing various search methods: vector (similarity), hybrid, and hybrid with semantic reranking search."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "df008be4e7af42b3a3db718d8619142b",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Evaluating:   0%|          | 0/20 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "{'context_precision': 0.7389, 'context_recall': 0.8667}"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from lib.utils import create_langchain_vectorstore\n",
    "\n",
    "similarity_search_qa = create_rag_chain(\n",
    "    generator_llm=generator_llm,\n",
    "    vectorstore=create_langchain_vectorstore(\n",
    "        azure_search_endpoint=endpoint,\n",
    "        azure_search_key=key_credential,\n",
    "        index_name=index_name,\n",
    "        embedding_function=text_3_large_embeddings.embed_query,\n",
    "        vector_field_name=\"vector_3_large\",\n",
    "        search_type=\"similarity\"\n",
    "    )\n",
    ")\n",
    "\n",
    "similarity_eval = evaluate_rag_chain(\n",
    "    test_dataset,\n",
    "    critic_llm,\n",
    "    text_3_large_embeddings,\n",
    "    similarity_search_qa)\n",
    "\n",
    "similarity_eval"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "53bb5f20719d4ff6b8d35e19717e4ffe",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Evaluating:   0%|          | 0/20 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "{'context_precision': 0.7389, 'context_recall': 0.8714}"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from lib.utils import create_langchain_vectorstore\n",
    "\n",
    "hybrid_search_qa = create_rag_chain(\n",
    "    generator_llm=generator_llm,\n",
    "    vectorstore=create_langchain_vectorstore(\n",
    "        azure_search_endpoint=endpoint,\n",
    "        azure_search_key=key_credential,\n",
    "        index_name=index_name,\n",
    "        embedding_function=text_3_large_embeddings.embed_query,\n",
    "        vector_field_name=\"vector_3_large\",\n",
    "        search_type=\"hybrid\"\n",
    "    )\n",
    ")\n",
    "\n",
    "hybrid_eval = evaluate_rag_chain(\n",
    "    test_dataset,\n",
    "    critic_llm,\n",
    "    text_3_large_embeddings,\n",
    "    hybrid_search_qa)\n",
    "\n",
    "hybrid_eval"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "accf1276420e49a0b9584aa0361a70c0",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Evaluating:   0%|          | 0/20 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "{'context_precision': 0.7389, 'context_recall': 0.8714}"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from lib.utils import create_langchain_vectorstore\n",
    "\n",
    "hybrid_semantic_search_qa = create_rag_chain(\n",
    "    generator_llm=generator_llm,\n",
    "    vectorstore=create_langchain_vectorstore(\n",
    "        azure_search_endpoint=endpoint,\n",
    "        azure_search_key=key_credential,\n",
    "        index_name=index_name,\n",
    "        embedding_function=text_3_large_embeddings.embed_query,\n",
    "        vector_field_name=\"vector_3_large\",\n",
    "        search_type=\"hybrid_semantic\"\n",
    "    )\n",
    ")\n",
    "\n",
    "hybrid_semantic_eval = evaluate_rag_chain(\n",
    "    test_dataset,\n",
    "    critic_llm,\n",
    "    text_3_large_embeddings,\n",
    "    hybrid_semantic_search_qa)\n",
    "\n",
    "hybrid_semantic_eval"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Visualize the results\n",
    "\n",
    "Graph context recall and context precision for the 3 search methods. Hybrid semantic has the best recall, while all the search methods are tied for precision."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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BSkhIUGLFi3Su+++m2UdJ06cUPPmzVW4cGENHTpU3t7eOnTokH788cds69+9e7caNmwoT09Pvf7668qfP78+//xzNWnSxBqirte/f38VLFhQI0eO1KFDhzRp0iRFRkZq3rx52a5n+fLlOnDggHr06KGAgADrJQq7d+/Whg0bZLFYbPo/++yzKlmypKKiorRt2zbNmDFDfn5+Gj9+vLVPr1699M0336hLly6qX7++fv/9dz311FPZ1nErGT9iFCxY0Np28eJFNW7cWP/8849efPFFFS9eXOvXr9ewYcN09OhRTZo0ydr3hRdeUHR0tFq2bKlevXrp6tWr+uOPP7RhwwbVrl1bkjR16lRVrlxZbdu2Vb58+bRo0SL17dtX6enp6tev3x3VL0kLFy6Uq6urOnTokKP+o0aN0ujRoxUSEqKXX35Ze/fu1dSpU7V582atW7dO+fPnt/Y9c+aMWrdureeee04dO3bU1KlT9dxzz+nbb7/VoEGD9NJLL6lLly764IMP1KFDBx05ckQFChSwWd+zzz6roKAgRUVFacOGDZoyZYrOnDlj8wNcbvbR3r171blzZ7344ovq3bu3ypcvf9PtjIqKsn7uk5KStGXLFm3btk1PPvmkJOm3335Ty5YtVapUKY0aNUqXLl3Sxx9/rAYNGmjbtm2ZLnnJyfsUAO5pBgDgrjp37pwhyXj66adz1D8uLs6QZPTq1cum/dVXXzUkGb///ru1rUSJEoYkY82aNda2EydOGM7OzsYrr7xibfv+++8NScbKlSttlnn+/HnD29vb6N27t037sWPHDC8vL5v2CxcuGGXKlDEqV65sXL582XjqqacMT09P4/Dhwzbzuru7G926dcvRth48eNCQZIwePdo4efKkcezYMWPVqlVGjRo1DEnG/PnzDcMwjFmzZhmSjMcff9y4evXqbdXfqFEjo0CBApnqTU9Pt/47Yz0HDx40DMMwfvrpJ0OSsXnz5my3Q5IxcuRI6/OwsDDDycnJSEhIsLb997//NQoUKGA0atQo0/pCQkJs6hg8eLDh6OhonD17Ntv1Xrx4MVPbd999l+k9MXLkSEOS0bNnT5u+7dq1M3x8fKzPM957ffv2tenXpUuXTNuYlaxezz/++MN49NFHDUnG999/b+07duxYw93d3fj7779tljF06FDD0dHRSExMNAzDMH7//XdDkjFg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LF9/26qNGzdqwoQJGjRokF555RW99tpr+uSTT7R69epM67pxPWbJaf2FCxdWo0aNNHPmTCUmJtr0ye7o2ZkzZzJNr169uiRlun1bBkdHRzVv3lw///yzzam9x48f15w5c/T444/nySmyGUcDb6zvxvdNbrRs2VKSMv2QcifLlK5d9/viiy9q6dKliouLk3TtKH1sbKyWLl2aqf/Zs2d19epVSdIzzzwjwzA0evToTP0ytj2rfXHu3DnNmjXrjuq+3ksvvaQiRYrolVde0d9//51p+okTJ/TOO+9IkkJCQuTk5KQpU6bY1PTll1/q3LlzdzwafFY+/fRTm+cff/yxpP+9pmbtoxtvT+fh4aEyZcpYPx9FihRR9erVNXv2bJu/C3/++aeWLVtm/XsHAA8SjnQDgB2ULl1ac+bMUadOnVSxYkVFRESoSpUqSk1N1fr16/X9999b730bHBysbt266YsvvtDZs2fVuHFjbdq0SbNnz1ZYWJiaNm2a6/VXr15djo6OGj9+vM6dOydnZ2fr/XqnTp2qrl27qmbNmnruuedUuHBhJSYmKiYmRg0aNNAnn3yiy5cvq1u3bipbtqz19lqjR4/WokWL1KNHD+3atcsa7GvVqqXffvtNEydOVNGiRVWyZMlMt8jKK56enjmqX7oWJB9//HHVrFlTffr0UcmSJXXo0CHFxMRYg+CNZs+erc8++0zt2rVT6dKldf78eU2fPl2enp7ZhoV33nlHy5cv1+OPP66+ffsqX758+vzzz5WSkqL3338/z7a9UaNGev/993XlyhUVK1ZMy5Yts97b/HZUr15dnTt31meffaZz586pfv36WrFihfbv33/H9Q4cOFCTJk3Se++9p7lz5+q1117TwoUL1bp1a+st7i5cuKBdu3bphx9+0KFDh+Tr66umTZuqa9eumjJlivbt22c9df6PP/5Q06ZNFRkZqebNm8vJyUlt2rTRiy++qOTkZE2fPl1+fn46evToHdcuXbuu+KefflKrVq1UvXp1Pf/886pVq5aka4PWfffdd9azWAoXLqxhw4Zp9OjRatGihdq2bau9e/fqs88+06OPPqrnn38+T2q63sGDB9W2bVu1aNFCsbGx1tu+BQcHS5Jp+6hSpUpq0qSJatWqpUKFCmnLli364YcfFBkZae3zwQcfqGXLlqpXr55eeOEF6y3DvLy8Mt3f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3n//fY0ZM0bLly9Xr169tGTJEh09etTa95dfftHFixfVqVMnm/nz5cunTZs2afLkyZo4caJmzJhhs44JEyYoODhY27dv1/DhwzPVcOTIEbVv315t2rRRXFycevXqpaFDh+Z4GxISEtSiRQs988wz2rlzp+bNm6e1a9cqMjJSkhQeHq5NmzYpISHBOs/u3bu1c+dOdenSRZJ0/vx5devWTWvXrtWGDRtUtmxZtWrVSufPn89xHQAAAACQFwjdD5Bq1app5MiRKlu2rCIiIlS7dm2tWLFC9evXV/ny5fX1119b+86aNUsdO3aUh4eHtS0wMFAfffSRypcvr/DwcPXv318fffSRzTqeeOIJvfLKKypdurRKly6dqYapU6eqdOnS+vDDD63L6d69e463ISoqSuHh4Ro0aJDKli2r+vXra8qUKfrqq690+fJlVa5cWcHBwZozZ451nm+//VZ169ZVmTJlrDU+//zzqlChgipWrKgvvvhCFy9e1OrVq3NcBwAAAADkBUL3A6RatWo2z4sUKaITJ05Iknr16qVZs2ZJko4fP65ff/1VPXv2tOn/2GOPyWKxWJ/Xq1dP+/btU1pamrWtdu3a2dYQHx+f6fTvevXq5XgbduzYoejoaHl4eFgfoaGhSk9P18GDByVdO9qdEboNw9B3332n8PBw6zKOHz+u3r17q2zZsvLy8pKnp6eSk5OVmJiY4zoAAAAAIC8wkNoDJH/+/DbPLRaL0tPTJUkREREaOnSoYmNjtX79epUsWVINGzbM9Trc3d3zpNabSU5O1osvvqgBAwZkmla8eHFJUufOnfXGG29o27ZtunTpko4cOWJzmny3bt3077//avLkySpRooScnZ1Vr149paammlo7AAAAANyI0P2Q8PHxUVhYmGbNmqXY2Fj16NEjU5+NGzfaPM+4HtrR0THH66lYsaIWLlyYaTk5VbNmTe3Zs8d6qnhWHnnkETVu3FjffvutLl26pCeffFJ+fn7W6evWrdNnn32mVq1aSbp2nfmpU6dyXAMAAAAA5BVOL3+I9OrVS7Nnz1Z8fLy6deuWaXpiYqKGDBmivXv36rvvvtPHH3+sgQMH5modL730kvbt26fXXntNe/fu1Zw5cxQdHZ3j+d944w2tX79ekZGRiouL0759+/Tzzz9bB1LLEB4errlz5+r777+3ObVcksqWLauvv/5a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",
      "text/plain": [
       "<Figure size 1000x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Data preparation\n",
    "labels = list(hybrid_semantic_eval.keys())\n",
    "hybrid_semantic_values = list(hybrid_semantic_eval.values())\n",
    "hybrid_values = list(hybrid_eval.values())\n",
    "similarity_values = list(similarity_eval.values())\n",
    "\n",
    "x = np.arange(len(labels))  # the label locations\n",
    "width = 0.2  # the width of the bars\n",
    "\n",
    "# Plotting\n",
    "fig, ax = plt.subplots(figsize=(10, 6))\n",
    "rects1 = ax.bar(x - width, hybrid_semantic_values, width, label='hybrid_semantic_eval')\n",
    "rects2 = ax.bar(x, hybrid_values, width, label='hybrid_eval')\n",
    "rects3 = ax.bar(x + width, similarity_values, width, label='similarity_eval')\n",
    "\n",
    "# Adding labels, title, and custom x-axis tick labels, etc.\n",
    "ax.set_xlabel('Metrics')\n",
    "ax.set_ylabel('Scores')\n",
    "ax.set_title('Context Precision and Recall Comparison')\n",
    "ax.set_xticks(x)\n",
    "ax.set_xticklabels(labels)\n",
    "ax.set_ylim(0, 1)\n",
    "ax.legend()\n",
    "\n",
    "fig.tight_layout()\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
